#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <asm/io.h>

static volatile unsigned int *CCM_CCGR1;

static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
// GPIO5_GDIR 地址：0x020AC004
static volatile unsigned int *GPIO5_GDIR;

//GPIO5_DR 地址：0x020AC000
static volatile unsigned int *GPIO5_DR;

/* 1. 确定主设备号                                                                 */
static int major = 0;

static struct class *led_class;



/* 3. 实现对应的open/read/write等函数，填入file_operations结构体                   */

static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int ret, val;

	ret = copy_from_user(&val, buf, 1);
	printk("%s %s line %d: val = %d\n", __FILE__, __FUNCTION__, __LINE__, val);
	if(val)
	{
		*GPIO5_DR |= (1<<3);
	}
	else
	{
		*GPIO5_DR &= ~(1<<3); 
	}
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/* 打开GPIO5时钟 */
	*CCM_CCGR1 |= (0x3<<30);

	/*配置为GPIO5_03*/
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0Xf;

	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;

	/*GPIO5_03配置为输出*/
	*GPIO5_GDIR |= (1<<3);

	return 0;
}

/* 2. 定义自己的file_operations结构体                                              */
static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.write   = led_drv_write,

};

/* 4. 把file_operations结构体告诉内核：注册驱动程序                                */
/* 5. 谁来注册驱动程序啊？得有一个入口函数：安装驱动程序时，就会去调用这个入口函数 */
static int __init led_init(void)
{
	int err;
	dev_t devid;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/* 初始化GPIO */
	CCM_CCGR1 = ioremap(0x20c406c, 4);

	IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x2290000 + 0x14, 4);

	GPIO5_GDIR = ioremap(0x20ac004, 4);

	GPIO5_DR = ioremap(0x20ac000, 4	);

	/* gpio设备初始化 */
	major = register_chrdev(0, "xxh_led", &led_drv);

	led_class = class_create(THIS_MODULE, "myled");  /* /sys/class/myled */
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "led");
		return -1;
	}
	
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled"); /* /dev/myled */
	
	return 0;
}

/* 6. 有入口函数就应该有出口函数：卸载驱动程序时，就会去调用这个出口函数           */
static void __exit led_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/* 释放iomap */
	iounmap(CCM_CCGR1);
	iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
	iounmap(GPIO5_GDIR);
	iounmap(GPIO5_DR);

	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
	unregister_chrdev(major, "xxh_led");
}


/* 7. 其他完善：提供设备信息，自动创建设备节点                                     */

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");


